package Servers

import (
	"PBMessage"
	"SGNet"

	. "Servers/BaseServer"
	proto "misc/protobuf/proto"
	log "misc/seelog"
	"strconv"
	"time"
	cfg "utils/config"
)

type HallServer struct {
	BaseServer
	Players    map[int64]IPlayer
	TickTime   int
	RoomSize   int
	MatchGame  *MatchGame
	ReadyRooms map[string]*ReadyRoom

	GateServer IServer
}

func NewHallServer() *HallServer {
	retHallServer := &HallServer{Players: make(map[int64]IPlayer), ReadyRooms: make(map[string]*ReadyRoom)}
	retHallServer.BaseServer.New()
	retHallServer.TickTime, _ = strconv.Atoi(cfg.Get()["halltick"])
	retHallServer.RoomSize, _ = strconv.Atoi(cfg.Get()["roomsize"])
	retHallServer.MatchGame = NewMatchGame(retHallServer)
	retHallServer.Init()
	return retHallServer
}

func (hallServer *HallServer) Init() {
	hallServer.Init_Account()
	hallServer.AddCommandHandler(CommonID_ClientLost, hallServer.ClientLost)
	hallServer.AddCommandHandler(CommonID_PlayerBackToHall, hallServer.CommonMessage_PlayerBackToHall)
	hallServer.AddNetHandler(PBMessage.PBID_CS_HallTick, func(msg *SGNet.Message) {})
	hallServer.AddNetHandler(PBMessage.PBID_CS_SyncReadyPlayerState, hallServer.CS_SyncReadyRoomPlayerStateHandler)
}

func (hallServer *HallServer) GetServerType() int {
	return Server_Hall
}

func (hallServer *HallServer) AddPlayer(player *HallPlayer) {
	hallServer.Players[player.GetUserId()] = player
	player.hallServer = hallServer
}

func (hallServer *HallServer) GetPlayerByAccount(account string) IPlayer {
	for _, player := range hallServer.Players {
		if player.GetAccountModel().Account == account {
			return player
		}
	}
	return nil
}

func (hallServer *HallServer) GetPlayer(userId int64) IPlayer {
	return hallServer.Players[userId]
}

func (hallServer *HallServer) RemovePlayer(userId int64) {
	delete(hallServer.Players, userId)
}

func (hallServer *HallServer) AddReadyRoom(readyroom *ReadyRoom) {
	hallServer.ReadyRooms[readyroom.UUID] = readyroom
}
func (hallServer *HallServer) GetReadyRoom(uuid string) *ReadyRoom {
	return hallServer.ReadyRooms[uuid]
}
func (hallServer *HallServer) RemoveReadyRoom(readyroom *ReadyRoom) {
	if readyroom != nil {
		readyroom.OnExit()
		delete(hallServer.ReadyRooms, readyroom.UUID)
	}
}

func (hallServer *HallServer) Run() {
	go hallServer.Loop()
}

func (hallServer *HallServer) Loop() {
	ticker := time.Tick(time.Millisecond)
	addMill := 0
	for _ = range ticker {
		hallServer.BaseServer.ProcessMessages()
		hallServer.MatchGame.Loop()
		for _, readyroom := range hallServer.ReadyRooms {
			readyroom.Update(0.01)
		}
		addMill++
		if addMill >= hallServer.TickTime {
			hallServer.HallTick()
			addMill = 0
		}
	}
}

func (hallServer *HallServer) HallTick() {
	for _, v := range hallServer.Players {
		tick := &PBMessage.SC_HallTick{}
		tick.ServerTime = proto.Int64(time.Now().UnixNano())
		tick.HallPlayerNumber = proto.Int32(int32(len(hallServer.Players)))
		v.GetSeesion().SendPacket(PBMessage.PBID_SC_HallTick, tick)
	}
}

func (hallServer *HallServer) ClientLost(msg *SGNet.Message) {
	if msg.Sess.UserData != nil {
		player := msg.Sess.UserData.(*HallPlayer)
		lastState := player.PlayerState
		player.PlayerState = EnumPlayerState_Lost
		switch lastState {
		case EnumPlayerState_ReadyRoom:
			player.readyRoom.BackPlayersToHall()
			hallServer.RemoveReadyRoom(player.readyRoom)
			log.Info("玩家在准备房间掉线 其他玩家踢回大厅")
		case EnumPlayerState_MatchGame:
			hallServer.MatchGame.Remove(player)
			log.Info("玩家匹配中掉线 从匹配队列中移除")
		}
		hallServer.RemovePlayer(player.GetUserId())
	}
}

func (hallServer *HallServer) CommonMessage_PlayerBackToHall(message *SGNet.Message) {
	player := message.Param["Player"].(IPlayer)
	hallPlayer := &HallPlayer{}
	hallPlayer.Account = player.GetAccountModel()
	hallPlayer.Sess = player.GetSeesion()
	hallPlayer.UserId = player.GetUserId()
	hallPlayer.PlayerState = EnumPlayerState_Hall
	hallPlayer.hallServer = hallServer
	hallServer.AddPlayer(hallPlayer)
}

func (hallServer *HallServer) CS_SyncReadyRoomPlayerStateHandler(msg *SGNet.Message) {
	syncReadyplayerState := &PBMessage.CS_SyncReadyPlayerState{}
	proto.Unmarshal(msg.Data, syncReadyplayerState)
	readyRoom := hallServer.GetReadyRoom(syncReadyplayerState.GetRoomID())
	if readyRoom == nil {
		log.Info("error roomId")
		return
	}
	readyRoom.CS_SyncReadyPlayerStateHandler(syncReadyplayerState)
}
